package com.bsj.power.system.config.interceptor;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.crypto.CryptoException;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.def.dto.open.OpenV1DTO;
import com.bsj.power.common.def.entity.portalLoginLog.PortalLoginLog;
import com.bsj.power.common.mapper.PortalLoginLogMapper;
import com.bsj.power.common.util.ExceptionUtil;
import com.bsj.power.common.util.IpUtils;
import com.bsj.power.system.config.annotation.OpenV1;
import com.bsj.power.system.config.wrapper.RequestWrapper;
import com.bsj.tools.HexStr;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author yinhao
 * @version 1.0
 * @description 开放接口参数拦截
 * @date 2024/7/10
 */
@Component
@Slf4j
public class OpenParamInterceptor implements HandlerInterceptor {

    @Value("${open.v1.aesKey}")
    private String aesKey;

    @Resource
    private PortalLoginLogMapper portalLoginLogMapper;


    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        // 如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();

        //拦截公共Api接口
        boolean openV1Method = method.getAnnotation(OpenV1.class) != null;
        if (openV1Method) {
            return paramOpenV1Auth(request, response);
        }
        return true;
    }

    private void returnOpenInvalid(HttpServletRequest request, HttpServletResponse response, PortalLoginLog portalLoginLog, JsonResult msg) throws IOException {
        portalLoginLog.setIp(IpUtils.getIp(request));
        portalLoginLog.setRemark(msg.getMsg());
        portalLoginLog.setLoginTime(new Date());
        portalLoginLog.setIsSuccess(NumericalConstants.TWO);
        portalLoginLogMapper.insert(portalLoginLog);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.setStatus(200);
        PrintWriter out = response.getWriter();
        out.print(JSON.toJSONString(msg));
    }

    public boolean paramOpenV1Auth(HttpServletRequest request, HttpServletResponse response) throws Exception {
        long millis = System.currentTimeMillis();
        String body = new RequestWrapper(request).getBody();
        log.info("OPEN-V1 登录验证请求参数: {}", body);
        PortalLoginLog portalLoginLog = new PortalLoginLog();
        if (StringUtils.isEmpty(body)) {
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.PARAM_FIELD_IS_ERROR));
            return false;
        }
        OpenV1DTO dto = JSON.parseObject(body, OpenV1DTO.class);
        if (dto == null) {
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.PARAM_FIELD_IS_ERROR));
            return false;
        }
        OpenV1DTO tempDto = new OpenV1DTO();
        BeanUtil.copyProperties(dto, tempDto);
        //需要将内容解密出来
        String loginid = dto.getLoginid();
        String workcode = dto.getWorkcode();
        Long timestamp = dto.getTimestamp();
        AES aes = SecureUtil.aes(HexStr.toArray(aesKey));
        try {
            if (loginid != null) {
                dto.setLoginid(aes.decryptStr(loginid));
            }
            if (workcode != null) {
                dto.setWorkcode(aes.decryptStr(workcode));
            }
        } catch (CryptoException e) {
            log.error("OPEN-V1 数据解密失败: {}", ExceptionUtil.getStackStr(e));
            portalLoginLog.setInParameter(JSON.toJSONString(dto));
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.DATA_DECRYPTION_FAILED));
            return false;
        }
        portalLoginLog.setLoginId(dto.getLoginid());
        portalLoginLog.setInParameter(getDecodeParam(tempDto));
        //设置三分钟限制
        if (millis - timestamp > 3 * 60 * 1000 || timestamp > millis) {
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.TIMED_OUT_REQUEST));
            return false;
        }
        Map<String, Object> map = BeanUtil.beanToMap(dto);
        if (map.size() < 5) {
            //必填参数未传
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.PARAM_FIELD_IS_ERROR));
            return false;
        }
        SortedMap<String, Object> params = new TreeMap<>(map);
        log.info("鉴权参数信息：{}", params);
        //移除请求的签名值
        Object sign = params.remove("sign");
        if (sign == null) {
            //没有签名
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.SIGN_CHECK_IS_ERROR));
            return false;
        }
        String signStr = "";
        Iterator<Map.Entry<String, Object>> iterator = params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            String key1 = next.getKey();
            Object value1 = next.getValue();
            if (value1 != null) {
                if (StringUtils.isEmpty(signStr)) {
                    signStr += key1 + "=" + value1;
                } else {
                    signStr += "&" + key1 + "=" + value1;
                }
            }
        }
        String serverSign = SecureUtil.md5(signStr).toUpperCase();
        log.error("OPEN-V1 服务端生成签名值: {}", serverSign);
        if (!serverSign.equals(sign)) {
            //验签失败
            returnOpenInvalid(request, response, portalLoginLog, JsonResult.fail(JsonResultEnum.SIGN_CHECK_IS_ERROR));
            return false;
        }
        return true;
    }

    /**
     * 获取解密后的参数
     *
     * @param dto
     * @return java.lang.String
     * @author ljx
     * @time 2024/10/14 17:23
     */
    private String getDecodeParam(OpenV1DTO dto) {
        AES aes = SecureUtil.aes(HexStr.toArray(aesKey));
        String loginid = aes.decryptStr(dto.getLoginid());
        String workcode = StringUtils.isNotBlank(dto.getWorkcode()) ? aes.decryptStr(dto.getWorkcode()) : "";
        OpenV1DTO openV1DTO = new OpenV1DTO(loginid, workcode, dto.getTimestamp(), dto.getSign(), dto.getRequestId());
        return JSON.toJSONString(openV1DTO);
    }

    public static void main(String[] args) {
        AES aes = SecureUtil.aes(HexStr.toArray("5AA98A41FBDBB6851DF769DB5C8F3C60"));
        String loginid = "muyang";
        String workcode = "muyanga";
        String encryptLoginid = aes.encryptHex(loginid);
        System.out.println("加密后的登录id：" + encryptLoginid);
        System.out.println("解密后的登录id：" + aes.decryptStr(encryptLoginid));

        String encryptWorkcode = aes.encryptHex(workcode);
        System.out.println("加密后的编号：" + encryptWorkcode);
        System.out.println("解密后的编号：" + aes.decryptStr(encryptWorkcode));

        long currentTimeMillis = System.currentTimeMillis();
        System.out.println("当前时间：" + currentTimeMillis);
        OpenV1DTO dto = new OpenV1DTO();
        dto.setLoginid("muyang");
        dto.setRequestId(currentTimeMillis + "");
        dto.setTimestamp(currentTimeMillis);
        dto.setWorkcode("muyanga");
        Map<String, Object> map = BeanUtil.beanToMap(dto);
        SortedMap<String, Object> params = new TreeMap<>(map);
        params.remove("sign");
        String signStr = "";
        Iterator<Map.Entry<String, Object>> iterator = params.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, Object> next = iterator.next();
            String key1 = next.getKey();
            Object value1 = next.getValue();
            if (StringUtils.isEmpty(signStr)) {
                signStr += key1 + "=" + value1;
            } else {
                signStr += "&" + key1 + "=" + value1;
            }
        }
        String serverSign = SecureUtil.md5(signStr).toUpperCase();
        System.out.println("签名：" + serverSign);
    }
}
